package com.flanz.apache.velocity.uimocker;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Simple HTTP 1.0 WebServer.
 * 
 * @author cf
 * 
 */

public class WebServer {
	private static boolean useThreadPoolExecutor = true;
	private static boolean useNonBlockingMode = true;

	/**
	 * @param args
	 * @throws IOException
	 */
	public static void main(String[] args) throws IOException {
		if (args.length < 1) {
			System.out.print("main configname.properties");
			return;
		}

		System.out.println("Start DIR: " + new File(".").getAbsolutePath());
		StartUpConfig startUpConfig = new StartUpConfig(args[0]);

		ProjectConfig projectConfig = createProjectConfig(startUpConfig);

		if (useNonBlockingMode) {
			WebServer.startNB(projectConfig);

		} else {
			WebServer.start(projectConfig);
		}
	}

	/**
	 * creates project config.
	 * 
	 * @param startUpConfig
	 * @return
	 * @throws FileNotFoundException
	 * @throws IOException
	 */

	private static ProjectConfig createProjectConfig(StartUpConfig startUpConfig)
			throws FileNotFoundException, IOException {
		MimeTable mimeTable = new MimeTable(
				startUpConfig.getMandatoryString(StartUpConfig.MIME_MAPPING));
		FileInputStream fileInputStreamAppSetting = new FileInputStream(
				startUpConfig.getMandatoryString(StartUpConfig.APP_VARS));

		Properties APP = new Properties();
		APP.load(fileInputStreamAppSetting);
		String projectDir = startUpConfig
				.getMandatoryString(StartUpConfig.PROJECT_DIR);
		String projectDocRoot = startUpConfig
				.getMandatoryString(StartUpConfig.PROJECT_DOCROOT);
		int serverPort = startUpConfig.getInteger(StartUpConfig.SERVER_PORT,
				StartUpConfig.SERVER_PORT_DEFAULT);
		int maxThread = startUpConfig.getInteger(
				StartUpConfig.MAX_REQUEST_THREADS,
				ProjectConfig.UNLIMITED_REQUEST_THREAD);
		
		String projectMacrosFileList = startUpConfig
				.getString(StartUpConfig.PROJECT_GLOBALMACROS_FILELIST, "");
		

		ProjectConfig projectConfig = new ProjectConfig();
		projectConfig.setServerPort(serverPort);
		projectConfig.setVM_APP_VARS(APP);
		projectConfig.setProjectDir(projectDir);
		projectConfig.setMimeTable(mimeTable);
		projectConfig.setHttpDocRoot(projectDocRoot);
		projectConfig.setMaxRequestThread(maxThread);
		projectConfig.setProjectMacrosFileList(projectMacrosFileList);
		
		return projectConfig;
	}

	/**
	 * Start WebServer non blocking. 1 Thread per Request.
	 */

	protected static void startNB(ProjectConfig projectConfig) {
		// Listen for incoming connections and handle them
		try {
			ServerSocket listener = startUpSocketBindings(projectConfig);
			int maxRequestThread = projectConfig.getMaxRequestThread();

			// Create the ThreadPoolExecutor
			BlockingQueue<Runnable> worksQueue = new ArrayBlockingQueue<Runnable>(
					maxRequestThread);
			MyRejectedExecutionHandler myRejectedExecutionHandler = new MyRejectedExecutionHandler();

			ThreadPoolExecutor executor = null;
			if (useThreadPoolExecutor) {
				executor = new ThreadPoolExecutor(maxRequestThread,
						maxRequestThread, 10, TimeUnit.SECONDS, worksQueue,
						myRejectedExecutionHandler);
			}

			for (; listener != null;)
				if ((WebServerThread.getConnectionCount() < maxRequestThread)
						|| (maxRequestThread == 0)) {
					Socket acceptSocket = listener.accept();
					WebServerThread connection = new WebServerThread(
							acceptSocket, projectConfig);

					if (useThreadPoolExecutor) {
						WebServerThread.threadStart();
						executor.execute(connection);
					} else {
						Thread t = new Thread(connection);
						WebServerThread.threadStart();
						t.start();
					}

				} else {
					try {
						Thread.sleep(200);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
		} catch (IOException ioe) {
			System.out.println("IOException on socket listen: " + ioe);
			ioe.printStackTrace();
		}
	}

	/**
	 * Blocking WebServer use Process Thread.
	 * 
	 * @param projectConfig
	 */

	protected static void start(ProjectConfig projectConfig) {
		ServerSocket s = startUpSocketBindings(projectConfig);

		for (; s != null;) {
			try {

				Socket accept = s.accept();

				InputStream inputStream = accept.getInputStream();
				OutputStream outputStream = accept.getOutputStream();

				HTTPRequestHandler.handleSocketConnection(
						projectConfig.getVM_APP_VARS(),
						projectConfig.getMimeTable(),
						projectConfig.getProjectDir(),
						projectConfig.getHttpDocRoot(),
						projectConfig.getProjectMacrosFileList(),
						inputStream,
						outputStream);

				accept.close();
			} catch (Exception e) {
				System.out.println("Error: " + e);
			}
		}
	}

	private static ServerSocket startUpSocketBindings(
			ProjectConfig projectConfig) {
		ServerSocket result;

		System.out.println("Webserver starting up on port "
				+ projectConfig.getServerPort());
		System.out.println("(press ctrl-c to exit)");

		try {
			// create the main server socket
			result = new ServerSocket(projectConfig.getServerPort());
		} catch (Exception e) {
			System.out.println("Error: " + e);
			return null;
		}

		System.out.println("Waiting for connection");
		return result;
	}

}
